﻿using System;
using System.Collections;
using System.Collections.Generic;

namespace QQ2564874169.RelationalSql
{
    public interface IDbSqlQuery : IDbQueryCore
    {
        T QueryScalar<T>(string sql, object param = null, bool isProc = false, int? timeout = null);
        IEnumerable<T> Query<T>(string sql, object param = null, bool isProc = false, int? timeout = null);
        IMultipleReader QueryMultiple(string sql, object param = null, bool isProc = false, int? timeout = null);
    }

    public class DbSqlQuery : IDbSqlQuery
    {
        public static event Action<IDbSqlQuery> Created;

        public Hashtable Data { get; private set; }
        public bool InTransaction => _sql.InTransaction;
        public event EventHandler<DbQueryBeforeEventArgs> Before;
        public event EventHandler<DbQueryAfterEventArgs> After;
        public event EventHandler<DbQueryErrorEventArgs> Error;

        private ISql _sql;

        public DbSqlQuery(ISql sql)
        {
            _sql = sql;
            Data = new Hashtable();
            Created?.Invoke(this);
        }

        public virtual void Dispose()
        {
            Data?.Clear();
            Data = null;
            _sql = null;
        }

        protected virtual void OnBefore(DbQueryBeforeEventArgs e)
        {
            Before?.Invoke(this, e);
        }

        protected virtual void OnAfter(DbQueryBeforeEventArgs e)
        {
            After?.Invoke(this, new DbQueryAfterEventArgs(e));
        }

        protected virtual void OnError(DbQueryErrorEventArgs e)
        {
            Error?.Invoke(this, e);
        }

        private T OnQuery<T>(DbQueryBeforeEventArgs before, Func<DbQueryBeforeEventArgs, T> query)
        {
            try
            {
                OnBefore(before);

                if (before.Result == null)
                {
                    before.Result = query(before);
                }
            }
            catch (Exception ex)
            {
                var error = new DbQueryErrorEventArgs(before, ex);
                OnError(error);
                if (error.Result != null)
                {
                    before.Result = error.Result;
                }
                else
                {
                    throw;
                }
            }
            finally
            {
                OnAfter(before);
            }
            return (T) before.Result;
        }

        public ITransactionScope BeginTransaction()
        {
            return _sql.BeginTransaction();
        }

        public T QueryScalar<T>(string sql, object param = null, bool isProc = false, int? timeout = null)
        {
            var before = new DbQueryBeforeEventArgs
            {
                Command = sql,
                Param = param,
                IsProc = isProc,
                Timeout = timeout
            };
            return OnQuery(before, e => _sql.QueryScalar<T>(e.Command, e.Param, e.IsProc, e.Timeout));
        }

        public IEnumerable<T> Query<T>(string sql, object param = null, bool isProc = false, int? timeout = null)
        {
            var before = new DbQueryBeforeEventArgs
            {
                Command = sql,
                Param = param,
                IsProc = isProc,
                Timeout = timeout
            };
            return OnQuery(before, e => _sql.Query<T>(e.Command, e.Param, e.IsProc, e.Timeout));
        }

        public IMultipleReader QueryMultiple(string sql, object param = null, bool isProc = false, int? timeout = null)
        {
            var before = new DbQueryBeforeEventArgs
            {
                Command = sql,
                Param = param,
                IsProc = isProc,
                Timeout = timeout
            };
            return OnQuery(before, e => _sql.QueryMultiple(e.Command, e.Param, e.IsProc, e.Timeout));
        }
    }
}
